package com.itheima.stock.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.itheima.stock.common.enums.ResponseCode;
import com.itheima.stock.mapper.SysPermissionMapper;
import com.itheima.stock.mapper.SysRoleMapper;
import com.itheima.stock.mapper.SysUserMapper;
import com.itheima.stock.mapper.SysUserRoleMapper;
import com.itheima.stock.pojo.SysPermission;
import com.itheima.stock.pojo.SysRole;
import com.itheima.stock.pojo.SysUser;
import com.itheima.stock.service.UserService;
import com.itheima.stock.utils.IdWorker;
import com.itheima.stock.vo.req.LoginReqVo;
import com.itheima.stock.vo.req.UserReqVo;
import com.itheima.stock.vo.resp.LoginRespVo;
import com.itheima.stock.vo.resp.PageResult;
import com.itheima.stock.vo.resp.R;
import com.itheima.stock.vo.resp.pojo.PermissionPojo;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ClassName UserServiceImpl
 * @Description TODO
 * @Author 久思
 * @Date 2023/9/24 19:37
 * @Version 1.0
 **/
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private SysPermissionMapper sysPermissionMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Override
    public R<LoginRespVo> login(LoginReqVo vo) {
        // 1. 检查参数
        if(StringUtils.isBlank(vo.getUsername()) || StringUtils.isBlank(vo.getPassword())) {
            return R.error(ResponseCode.SYSTEM_PASSWORD_ERROR.getMessage());
        }

        // 2. 校验验证码
        String checkCode = redisTemplate.opsForValue().get(vo.getRkey());
        if(StringUtils.isBlank(vo.getCode()) || !checkCode.equals(vo.getCode())) {
            return R.error("验证码验证失败");
        }

        // 3. 判断用户是否存在
            // 3.1 查询用户信息是否存在
            SysUser sysUser = sysUserMapper.findByUserName(vo.getUsername());
            // 判断用户是否存在
            if(sysUser == null) {
                return R.error(ResponseCode.SYSTEM_USERNAME_NOT_EXISTS.getMessage());
            }

        // 4. 校验密码是否一致
        if(!passwordEncoder.matches(vo.getPassword(), sysUser.getPassword())) {
            return R.error(ResponseCode.SYSTEM_PASSWORD_ERROR.getMessage());
        }

        // 5. 返回响应结果
        LoginRespVo loginRespVo = new LoginRespVo();
        BeanUtils.copyProperties(sysUser, loginRespVo);

        List<SysPermission> permissions = sysPermissionMapper.queryTreeNodes(sysUser.getId());
        // 用户权限详情完善
        List<PermissionPojo> tree = getTree(permissions, "0", true);
        loginRespVo.setMenus(tree);

        // 过滤出来权限集合
        List<String> authBtnInfo = permissions.stream()
                .filter(per -> Strings.isNullOrEmpty(per.getCode()) && per.getType() == 3)
                .map(per -> per.getCode()).collect(Collectors.toList());

        loginRespVo.setPermissions(authBtnInfo);

        return R.ok(loginRespVo);
    }

    /**
     * 多条件综合查询用户分页信息，条件包含：分页信息 用户创建日期范围
     *
     * @param vo
     * @return
     */
    @Override
    public R<PageResult<SysUser>> queryUserInfo(UserReqVo vo) {
        PageHelper.startPage(vo.getPageNum(), vo.getPageSize());

        // 根据参数多条件查询用户信息
        List<SysUser> userInfoList = sysUserMapper.findUserInfo(vo);

        PageInfo<SysUser> userPageInfo = new PageInfo<>(userInfoList);

        PageResult<SysUser> userPageResult = new PageResult<>(userPageInfo);

        return R.ok(userPageResult);
    }

    @Transactional
    @Override
    public R<String> insertUserInfo(SysUser sysUser) {
        // 先根据用户名查询用户是否已经存在
        // 查询
        SysUser userInfo = sysUserMapper.findByUserName(sysUser.getUsername());
        // 判断用户是否已经存在
        if(userInfo != null) {
            return R.error(ResponseCode.ACCOUNT_EXISTS_ERROR.getMessage());
        }

        // 封装参数
        // 使用idWork生成id
        long id = idWorker.nextId();
        sysUser.setId(String.valueOf(id));
        // 获取当前时间
        sysUser.setCreateTime(new Date());
        // 将密码根据MD5算法进行加密，然后装填到实体类中
        String pwdUp = DigestUtils.md5DigestAsHex(sysUser.getPassword().getBytes());
        sysUser.setPassword(pwdUp);

        sysUserMapper.insert(sysUser);

        return R.ok("操作成功");
    }

    /**
     * 根据用户id查询用户所分配的角色 查询所有的角色
     * @param userId
     * @return
     */
    @Override
    public R<Map<String, Object>> queryUserRoleInfo(String userId) {
        // 查询所有角色信息
        List<SysRole> roleInfoAll = sysRoleMapper.findAll();

        // 根据用户查询查询用户和角色关联id表 根据查询出的角色id查询出当前用户分配的角色
        List<Long> roleIdList = sysUserRoleMapper.getById(userId);

        // 根据查询到的角色id查询当前用户分配的角色信息
        List<SysRole> roleInfoDistribution = null;
        if(roleIdList.size() == 0) {
            roleInfoDistribution = new ArrayList<>();
        } else {
            roleInfoDistribution = sysRoleMapper.getById(roleIdList);
        }

        // 封装结果返回
        Map<String, Object> mapResult = new HashMap<>();
        mapResult.put("ownRoleIds", roleIdList);
        mapResult.put("allRole", roleInfoAll);
        return R.ok(mapResult);
    }

    /**
     * 修改页面回显信息
     * @param userId
     * @return
     */
    @Override
    public R<SysUser> getById(Long userId) {
        SysUser sysUser = sysUserMapper.finById(userId);
        return R.ok(sysUser);
    }

    /**
     * 修改用户信息
     * @param sysUser
     * @return
     */
    @Override
    public R<String> update(SysUser sysUser) {
        // 封装参数
        sysUser.setUpdateTime(new Date());

        sysUserMapper.updateByPrimaryKey(sysUser);
        return R.ok("操作成功");
    }

    /**
     * 批量删除用户信息
     * @param ids
     * @return
     */
    @Override
    public R<String> delete(List<Long> ids) {
        sysUserMapper.deleteByPrimaryKey(ids);
        return R.ok("操作成功");
    }

    @Autowired
    private IdWorker idWorker;

    @Override
    public R<Map> generateCaptcha() {
        // 生成随机4位验证码
        String checkCode = RandomStringUtils.randomNumeric(4);

        // 生成全局唯一id
        String rkey = String.valueOf(idWorker.nextId());

        // 验证码存入redis中，并设置有效期位一分钟
        redisTemplate.opsForValue().set(rkey, checkCode, 60, TimeUnit.SECONDS);

        // 封装结果返回
        Map<String, String> mapResult = new HashMap<>();
        mapResult.put("rkey", rkey);
        mapResult.put("code", checkCode);
        return R.ok(mapResult);
    }

    /**
     * 构建权限树方法：递归
     * @param permissions 权限集合
     * @param pid 顶层父id
     * @param isOnlyMenuType 是否有下一级
     * @return
     */
    public List<PermissionPojo> getTree(List<SysPermission> permissions, String pid, boolean isOnlyMenuType) {
        ArrayList<PermissionPojo> list = Lists.newArrayList();
        if (CollectionUtils.isEmpty(permissions)) {
            return list;
        }

        for (SysPermission permission : permissions) {
            if(permission.getPid().equals(pid)) {
                if(permission.getType().intValue() != 3 || !isOnlyMenuType) {
                    PermissionPojo permissionPojo = new PermissionPojo();
                    permissionPojo.setId(permission.getId());
                    permissionPojo.setPid(permission.getPid());
                    permissionPojo.setTitle(permission.getTitle());
                    permissionPojo.setIcon(permission.getIcon());
                    permissionPojo.setPath(permission.getPath());
                    permissionPojo.setName(permission.getName());
                    permissionPojo.setChildren(getTree(permissions, permission.getId(), isOnlyMenuType));
                    list.add(permissionPojo);

                }
            }
        }
        return list;
    }
}
